/*
   Copyright (c) 2012 LinkedIn Corp.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package com.linkedin.restli.client.util;


import com.linkedin.data.schema.PathSpec;
import com.linkedin.data.template.DataTemplate;
import com.linkedin.data.transform.patch.request.PatchTree;


/**
 * Generate a {@link PatchTree} using the existing API of a {@link com.linkedin.data.template.DataTemplate}.
 *
 * <p> Invocations to the record template returned by {@link #getRecordingProxy()} are recorded and persisted in the
 * patch generated by {@link #generatePatchTree()}. The interpretation of passing {@code null} to a setter depends on
 * the {@link com.linkedin.data.template.SetMode} passed in, defaulting to
 * {@link com.linkedin.data.template.SetMode#DISALLOW_NULL} if not specified. Invoking a getter which returns a
 * {@link com.linkedin.data.template.RecordTemplate} will automatically instantiate the object as a proxy, recording
 * sets and removes which will show up in the base {@link #generatePatchTree()}. Invoking any other getter will throw an
 * {@link IllegalArgumentException}. As such, {@link com.linkedin.data.template.GetMode} parameters are ignored.
 *
 * <p> The API is used as follows:
 *
 * {@code
 * PatchTreeRecorder<GeoLocation> pc = new PatchTreeRecorder<GeoLocation>(GeoLocation.class);
 * GeoLocation geoLocationTemplate = pc.getRecordingProxy();
 * geoLocationTemplate.setRegionCode(100).setUsesDaylightSavings(true).setLatLong(null, SetMode.REMOVE_IF_NULL).removeGmtOffset();
 * geoLocationTemplate.getLocation().setPostalCode("95128");
 *
 * PatchTree patchTree = pc.generatePatchTree();
 * }
 *
 * @author jflorencio
 */
public class PatchTreeRecorder<T extends DataTemplate<?>>
{
  private final T _templateProxy;
  private final PatchTree _patchTree;

  public PatchTreeRecorder(Class<T> clazz)
  {
    _patchTree = new PatchTree();
    _templateProxy = GeneratePatchProxyFactory.newInstance(clazz, _patchTree, PathSpec.emptyPath());
  }

  /**
   * @return a proxy recording each set and remove method invocation to store in a {@link PatchTree}.
   */
  public T getRecordingProxy()
  {
    return _templateProxy;
  }

  /**
   * @return a new patch tree with a copy of the state recorded by the record template proxy returned from the
   *         {@link #getRecordingProxy()} method.
   */
  public PatchTree generatePatchTree()
  {
    try
    {
      return new PatchTree( _patchTree.getDataMap().copy());
    }
    catch (CloneNotSupportedException e)
    {
      throw new IllegalStateException("Error copying data map: " + _patchTree.getDataMap(), e);
    }
  }
}